دليل شامل يقارن أبرز مكتبات عملاء HTTP في بايثون. تعرف على متى تستخدم Requests أو httpx أو urllib3 لمشاريعك، مع أمثلة برمجية ورؤى حول الأداء.
عملاء HTTP في بايثون: مقارنة معمقة بين Requests و httpx و urllib3
في عالم تطوير البرمجيات الحديث، الاتصال هو المفتاح. نادرًا ما توجد التطبيقات بمعزل عن غيرها؛ فهي تتحدث إلى قواعد البيانات، والخدمات الخارجية، والخدمات المصغرة الأخرى، بشكل أساسي عبر واجهات برمجة التطبيقات (APIs) عبر بروتوكول نقل النص التشعبي (HTTP). بالنسبة لمطوري بايثون، يُعد إجراء طلبات HTTP هذه مهمة أساسية، ويمكن أن تؤثر المكتبة التي تختارها لهذه المهمة بشكل كبير على إنتاجيتك، وأداء التطبيق، وسهولة صيانة الكود.
يوفر نظام بايثون البيئي مجموعة غنية من الأدوات لهذا الغرض، ولكن ثلاثة أسماء تبرز باستمرار: urllib3، الأساس القوي؛ و Requests، المعيار المحبوب عالميًا؛ و httpx، المنافس الحديث القادر على التعامل مع العمليات غير المتزامنة. لا يتعلق الاختيار بينها بإيجاد المكتبة "الأفضل" الوحيدة، بل بفهم نقاط قوتها الفريدة واختيار الأداة المناسبة لاحتياجاتك الخاصة. سيوفر هذا الدليل مقارنة معمقة واحترافية لمساعدتك في اتخاذ هذا القرار المستنير.
فهم الأساس: ما هو عميل HTTP؟
في جوهره، عميل HTTP هو جزء من برنامج مصمم لإرسال طلبات HTTP إلى خادم ومعالجة استجابات HTTP التي يتلقاها. يخفي هذا التعريف البسيط قدرًا كبيرًا من التعقيد. تتعامل مكتبة عميل HTTP القوية مع العديد من التفاصيل منخفضة المستوى، بما في ذلك:
- إدارة مآخذ الشبكة والاتصالات.
- تنسيق طلبات HTTP بشكل صحيح مع الرؤوس، النصوص، والأساليب (GET، POST، PUT، إلخ).
- التعامل مع عمليات إعادة التوجيه والمهلات.
- إدارة ملفات تعريف الارتباط والجلسات للاتصال الذي يحافظ على الحالة.
- التعامل مع ترميزات المحتوى المختلفة (مثل JSON أو بيانات النماذج).
- التعامل مع SSL/TLS لاتصالات HTTPS الآمنة.
- إعادة استخدام الاتصالات لتحسين الأداء (تجميع الاتصالات).
بينما تتضمن مكتبة بايثون القياسية وحدات مثل urllib.request
، فإنها غالبًا ما تُعتبر منخفضة المستوى جدًا ومُعقدة للاستخدام اليومي. وقد أدى هذا إلى تطوير مكتبات خارجية أكثر قوة وسهولة في الاستخدام والتي تجرد هذا التعقيد، مما يسمح للمطورين بالتركيز على منطق تطبيقهم.
البطل الكلاسيكي: urllib3
قبل أن نناقش المكتبات عالية المستوى، من الضروري فهم urllib3
. إنها إحدى أكثر الحزم التي تم تنزيلها على PyPI، ليس لأن معظم المطورين يستخدمونها مباشرة، بل لأنها المحرك القوي والموثوق الذي يشغل عددًا لا يحصى من المكتبات الأخرى عالية المستوى، وأبرزها Requests.
ما هي urllib3
؟
urllib3
هو عميل HTTP قوي يركز على سلامة الأداء للغة بايثون. يركز بشكل أساسي على توفير أساس موثوق وفعال لاتصال HTTP. لم يتم تصميمه بنفس التركيز على أناقة واجهة برمجة التطبيقات مثل Requests، بل على الدقة والأداء والتحكم الدقيق.
الميزات والقوة الرئيسية
- تجميع الاتصالات (Connection Pooling): يمكن القول إن هذه هي أهم ميزة فيه. يدير
urllib3
مجمعات من الاتصالات. عندما تقوم بإجراء طلب إلى مضيف قمت بالاتصال به من قبل، فإنه يعيد استخدام اتصال موجود بدلاً من إنشاء اتصال جديد. هذا يقلل بشكل كبير من زمن الاستجابة للطلبات المتتالية، حيث يتم تجنب تكلفة عملية تأكيد الاتصال لبروتوكولي TCP و TLS. - أمان الخيوط (Thread Safety): يمكن مشاركة كائن
PoolManager
واحد عبر خيوط متعددة، مما يجعله خيارًا قويًا للتطبيقات متعددة الخيوط. - معالجة الأخطاء القوية وإعادة المحاولة: يوفر آليات متطورة لإعادة محاولة الطلبات الفاشلة، مع استراتيجيات تأخير قابلة للتكوين، وهو أمر بالغ الأهمية لبناء تطبيقات مرنة تتواصل مع خدمات قد تكون غير مستقرة.
- تحكم دقيق: يعرض ثروة من خيارات التكوين، مما يسمح للمطورين بضبط المهلات، والتحقق من TLS، وإعدادات الوكيل، والمزيد.
- تحميل الملفات: لديه دعم ممتاز لترميز multipart form-data، مما يسهل تحميل الملفات بكفاءة.
مثال على الكود: إجراء طلب GET
استخدام urllib3
أكثر تفصيلاً من نظرائه عالية المستوى، ولكنه لا يزال مباشرًا. عادة ما تتفاعل مع كائن PoolManager
.
import urllib3
import json
# It's recommended to create a single PoolManager instance and reuse it
http = urllib3.PoolManager()
# Define the target URL
url = "https://api.github.com/users/python"
# Make the request
# Note: The request method is passed as a string ('GET')
# The response object is an HTTPResponse instance
response = http.request("GET", url, headers={"User-Agent": "My-Urllib3-App/1.0"})
# Check the response status
if response.status == 200:
# The data is returned as a bytes object and needs to be decoded
data_bytes = response.data
data_str = data_bytes.decode("utf-8")
# Manually parse the JSON
user_data = json.loads(data_str)
print(f"User Name: {user_data['name']}")
print(f"Public Repos: {user_data['public_repos']}")
else:
print(f"Error: Received status code {response.status}")
# The connection is automatically released back to the pool
متى تستخدم urllib3
- عند بناء مكتبة أو إطار عمل يحتاج إلى إجراء طلبات HTTP وترغب في إدارة التبعيات بدقة.
- عندما تحتاج إلى أقصى أداء وتحكم في إدارة الاتصالات ومنطق إعادة المحاولة.
- في الأنظمة القديمة أو البيئات المقيدة حيث تحتاج إلى الاعتماد على مكتبة غالبًا ما تكون مضمنة (مرفقة) داخل حزم رئيسية أخرى.
الحكم على urllib3
الإيجابيات: عالي الأداء، آمن للخيوط، قوي، ويوفر تحكمًا عميقًا في دورة حياة الطلب.
السلبيات: واجهة برمجة التطبيقات مطولة وأقل سهولة. تتطلب عملًا يدويًا للمهام الشائعة مثل فك تشفير JSON وترميز معلمات الطلب.
خيار الجمهور: requests
- "HTTP للبشر"
لأكثر من عقد من الزمان، كانت requests
المعيار الفعلي لإجراء طلبات HTTP في بايثون. شعارها الشهير، "HTTP للبشر"، يلخص تمامًا فلسفة تصميمها. توفر واجهة برمجة تطبيقات جميلة، بسيطة، وأنيقة تخفي التعقيد الأساسي الذي تديره urllib3
.
ما هي requests
؟
requests
هي مكتبة HTTP عالية المستوى تركز على تجربة المطور وسهولة الاستخدام. إنها تغلف قوة urllib3
في واجهة سهلة الاستخدام، مما يجعل المهام الشائعة بسيطة بشكل لا يصدق مع الاستمرار في توفير الوصول إلى ميزات قوية عند الحاجة.
الميزات والقوة الرئيسية
- واجهة برمجة تطبيقات بسيطة وأنيقة: الواجهة ممتعة للعمل بها. إجراء طلب GET هو سطر واحد من التعليمات البرمجية سهل القراءة.
- كائنات الجلسة (Session Objects): كائنات الجلسة هي ميزة أساسية. إنها تحتفظ بالمعلمات عبر الطلبات، وتدير ملفات تعريف الارتباط تلقائيًا، والأهم من ذلك، تستخدم تجميع الاتصالات الخاص بـ
urllib3
تحت الغطاء. استخدامSession
هو الطريقة الموصى بها لتحقيق أداء عالٍ معrequests
. - فك تشفير JSON المدمج: التفاعل مع واجهات برمجة تطبيقات JSON أمر بسيط. يحتوي كائن الاستجابة على طريقة
.json()
تقوم تلقائيًا بفك تشفير نص الاستجابة وإرجاع قاموس بايثون أو قائمة. - فك ضغط المحتوى التلقائي: يتعامل بشفافية مع بيانات الاستجابة المضغوطة (gzip, deflate)، لذلك لا داعي للتفكير في الأمر.
- التعامل السلس مع البيانات المعقدة: إرسال بيانات النماذج أو حمولات JSON بسيط مثل تمرير قاموس إلى المعلمة
data
أوjson
. - النطاقات وعناوين URL الدولية: دعم ممتاز وجاهز للاستخدام لشبكة ويب عالمية.
مثال على الكود: إجراء طلب GET والتعامل مع JSON
قارن بساطة هذا المثال بإصدار urllib3
. لاحظ عدم وجود فك تشفير يدوي أو تحليل JSON.
import requests
# The recommended approach for multiple requests to the same host
with requests.Session() as session:
session.headers.update({"User-Agent": "My-Requests-App/1.0"})
url = "https://api.github.com/users/python"
try:
# Making the request is a single function call
response = session.get(url)
# Raise an exception for bad status codes (4xx or 5xx)
response.raise_for_status()
# The .json() method handles decoding and parsing
user_data = response.json()
print(f"User Name: {user_data['name']}")
print(f"Public Repos: {user_data['public_repos']}")
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
متى تستخدم requests
- لغالبية مهام HTTP المتزامنة في التطبيقات، والاسكريبتات، ومشاريع علم البيانات.
- عند التفاعل مع واجهات برمجة تطبيقات REST.
- للنماذج الأولية السريعة وبناء الأدوات الداخلية.
- عندما يكون هدفك الأساسي هو سهولة قراءة الكود وسرعة التطوير لعمليات الإدخال/الإخراج الشبكية المتزامنة.
قيود يجب مراعاتها
أكبر قيود requests
في العصر الحديث هو أن واجهة برمجة التطبيقات الخاصة بها هي متزامنة بشكل صارم. فهي تحجب التنفيذ حتى يتم استلام الاستجابة. هذا يجعلها غير مناسبة للتطبيقات عالية التزامن المبنية على أطر عمل غير متزامنة مثل asyncio
، FastAPI، أو Starlette. بينما يمكنك استخدامها في مجمع خيوط (thread pool)، فإن هذا النهج أقل كفاءة من الإدخال/الإخراج غير المتزامن الأصلي للتعامل مع آلاف الاتصالات المتزامنة.
الحكم على requests
الإيجابيات: سهلة الاستخدام بشكل لا يصدق، قابلة للقراءة بدرجة عالية، مجموعة ميزات غنية، مجتمع ضخم، وتوثيق ممتاز.
السلبيات: متزامنة فقط. هذا عيب كبير للتطبيقات الحديثة عالية الأداء المعتمدة على الإدخال/الإخراج.
المنافس الحديث: httpx
- الخليفة الجاهز للعمليات غير المتزامنة
httpx
هو عميل HTTP حديث وكامل الميزات ظهر لمعالجة قيود requests
، وبشكل أساسي افتقارها لدعم العمليات غير المتزامنة. تم تصميمه ليكون عميل الجيل التالي، يتبنى ميزات بايثون الحديثة وبروتوكولات الويب بينما يقدم واجهة برمجة تطبيقات مألوفة لأولئك القادمين من requests
.
ما هي httpx
؟
httpx
هو عميل HTTP متعدد الاستخدامات لبايثون يوفر واجهة برمجة تطبيقات متزامنة وغير متزامنة. ميزته القاتلة هي دعمه من الدرجة الأولى لبناء الجملة async/await
. علاوة على ذلك، فإنه يوفر دعمًا لبروتوكولات الويب الحديثة مثل HTTP/2 و HTTP/3، والتي يمكن أن توفر تحسينات كبيرة في الأداء.
الميزات والقوة الرئيسية
- دعم المتزامن وغير المتزامن: هذه هي ميزته الأساسية. يمكنك استخدام نفس المكتبة وواجهة برمجة تطبيقات مشابهة جدًا لكل من السكريبتات المتزامنة التقليدية والتطبيقات غير المتزامنة عالية الأداء. يوحد هذا التوحيد إدارة التبعيات ويقلل من منحنى التعلم.
- دعم HTTP/2 و HTTP/3: بخلاف
requests
، يمكن لـhttpx
التعامل مع HTTP/2. يسمح هذا البروتوكول بتعدد الإرسال (multiplexing) — إرسال طلبات واستجابات متعددة عبر اتصال واحد في وقت واحد — مما يمكن أن يسرع بشكل كبير الاتصال بالخوادم الحديثة التي تدعمه. - واجهة برمجة تطبيقات متوافقة مع
requests
: تم تصميم واجهة برمجة التطبيقات عمدًا لتكون بديلاً مباشرًا لـrequests
في العديد من الحالات. وظائف مثلhttpx.get()
وكائنات مثلhttpx.Client()
(المكافئ لـrequests.Session()
) ستشعر بأنها مألوفة على الفور. - واجهة برمجة تطبيقات نقل قابلة للتوسيع: تحتوي على واجهة برمجة تطبيقات نقل نظيفة ومحددة جيدًا، مما يسهل كتابة محولات مخصصة لأشياء مثل المحاكاة، التخزين المؤقت، أو بروتوكولات الشبكة المخصصة.
أمثلة الكود: متزامن، غير متزامن، والعملاء
أولاً، مثال متزامن. لاحظ كيف أنه مطابق تقريبًا لكود requests
.
# Synchronous httpx code
import httpx
url = "https://api.github.com/users/python-httpx"
with httpx.Client(headers={"User-Agent": "My-HTTPX-App/1.0"}) as client:
try:
response = client.get(url)
response.raise_for_status()
user_data = response.json()
print(f"(Sync) User Name: {user_data['name']}")
print(f"(Sync) Public Repos: {user_data['public_repos']}")
except httpx.RequestError as e:
print(f"An error occurred: {e}")
الآن، الإصدار غير المتزامن. الهيكل هو نفسه، ولكنه يستفيد من async/await
لأداء عمليات الإدخال/الإخراج غير الحاصرة.
# Asynchronous httpx code
import httpx
import asyncio
async def fetch_github_user():
url = "https://api.github.com/users/python-httpx"
# Use AsyncClient for async operations
async with httpx.AsyncClient(headers={"User-Agent": "My-HTTPX-App/1.0"}) as client:
try:
# The 'await' keyword pauses execution until the network call completes
response = await client.get(url)
response.raise_for_status()
user_data = response.json()
print(f"(Async) User Name: {user_data['name']}")
print(f"(Async) Public Repos: {user_data['public_repos']}")
except httpx.RequestError as e:
print(f"An error occurred: {e}")
# Run the async function
asyncio.run(fetch_github_user())
متى تستخدم httpx
- لأي مشروع بايثون جديد يبدأ اليوم. طبيعته المزدوجة (متزامن/غير متزامن) تجعله الخيار الأكثر مرونة وملاءمة للمستقبل. حتى إذا كنت تحتاج فقط إلى طلبات متزامنة اليوم، فإن استخدام `httpx` يعني أنك جاهز للانتقال السلس إلى الوضع غير المتزامن إذا تطورت احتياجات تطبيقك. إنه الخيار الواضح لأي مشروع يتضمن أطر عمل ويب حديثة أو يتطلب مستويات عالية من التزامن.
- عند بناء تطبيقات باستخدام أطر عمل غير متزامنة مثل FastAPI، Starlette، Sanic، أو Django 3+.
- عندما تحتاج إلى إجراء عدد كبير من طلبات الإدخال/الإخراج المتزامنة (على سبيل المثال، استدعاء آلاف واجهات برمجة التطبيقات).
- عندما تحتاج إلى التواصل مع الخوادم التي تستفيد من HTTP/2 لتحسين الأداء.
الحكم على httpx
الإيجابيات: يوفر واجهات برمجة تطبيقات متزامنة وغير متزامنة، ويدعم HTTP/2، ويتميز بتصميم حديث ونظيف، ويوفر واجهة برمجة تطبيقات مألوفة لمستخدمي requests
.
السلبيات: كونه مشروعًا أحدث، فإن نظامه البيئي من الإضافات الخارجية ليس واسعًا مثل requests
، على الرغم من أنه ينمو بسرعة.
مقارنة الميزات: في لمحة
يوفر هذا الملخص مرجعًا سريعًا للفروق الرئيسية بين المكتبات الثلاث.
الميزة: واجهة برمجة تطبيقات عالية المستوى وسهلة الاستخدام
- urllib3: لا. منخفضة المستوى ومُطولة.
- requests: نعم. هذه هي قوتها الأساسية.
- httpx: نعم. مصممة لتكون مألوفة لمستخدمي `requests`.
الميزة: واجهة برمجة تطبيقات متزامنة
- urllib3: نعم.
- requests: نعم.
- httpx: نعم.
الميزة: واجهة برمجة تطبيقات غير متزامنة (async/await
)
- urllib3: لا.
- requests: لا.
- httpx: نعم. هذا هو عامل التمييز الرئيسي لها.
الميزة: دعم HTTP/2
- urllib3: لا.
- requests: لا.
- httpx: نعم.
الميزة: تجميع الاتصالات
- urllib3: نعم. ميزة أساسية.
- requests: نعم (عبر كائنات `Session`).
- httpx: نعم (عبر كائنات `Client` و `AsyncClient`).
الميزة: فك تشفير JSON مدمج
- urllib3: لا. يتطلب فك تشفير وتحليل يدوي.
- requests: نعم (عبر
response.json()
). - httpx: نعم (عبر
response.json()
).
اعتبارات الأداء
عند مناقشة الأداء، السياق هو كل شيء. بالنسبة لطلب واحد وبسيط، سيكون الفرق في الأداء بين هذه المكتبات الثلاث ضئيلًا ومن المحتمل أن يضيع في زمن استجابة الشبكة.
تظهر الفروقات في الأداء حقًا في التعامل مع التزامن:
- `requests` في بيئة متعددة الخيوط: هذه هي الطريقة التقليدية لتحقيق التزامن مع `requests`. إنها تعمل، لكن الخيوط لها عبء ذاكرة أعلى ويمكن أن تعاني من تكاليف تبديل السياق، خاصة مع نمو عدد المهام المتزامنة إلى المئات أو الآلاف.
- `httpx` مع `asyncio`: بالنسبة للمهام المحددة بالإدخال/الإخراج مثل إجراء استدعاءات API، فإن `asyncio` أكثر كفاءة بكثير. يستخدم خيطًا واحدًا وحلقة أحداث لإدارة آلاف الاتصالات المتزامنة بحد أدنى من العبء الزائد. إذا كان تطبيقك يحتاج إلى الاستعلام عن مئات الخدمات المصغرة في وقت واحد، فإن `httpx` سيتفوق بشكل كبير على تطبيق `requests` متعدد الخيوط.
علاوة على ذلك، يمكن أن يوفر دعم `httpx` لـ HTTP/2 دفعة إضافية في الأداء عند الاتصال بخادم يدعمه أيضًا، لأنه يسمح بإرسال طلبات متعددة عبر نفس اتصال TCP دون انتظار الاستجابات، مما يقلل من زمن الاستجابة.
اختيار المكتبة المناسبة لمشروعك
بناءً على هذا التحليل المتعمق، إليك توصياتنا القابلة للتنفيذ للمطورين حول العالم:
استخدم `httpx` إذا...
كنت تبدأ أي مشروع بايثون جديد في عام 2023 أو ما بعده. طبيعته المزدوجة (متزامن/غير متزامن) تجعله الخيار الأكثر مرونة وملاءمة للمستقبل. حتى إذا كنت تحتاج فقط إلى طلبات متزامنة اليوم، فإن استخدام `httpx` يعني أنك جاهز للانتقال السلس إلى الوضع غير المتزامن إذا تطورت احتياجات تطبيقك. إنه الخيار الواضح لأي مشروع يتضمن أطر عمل ويب حديثة أو يتطلب مستويات عالية من التزامن.
استخدم `requests` إذا...
كنت تعمل على قاعدة كود قديمة تستخدم `requests` بالفعل بشكل مكثف. قد لا تكون تكلفة الترحيل تستحق الفائدة إذا كان التطبيق مستقرًا ولا توجد متطلبات تزامن. كما يظل خيارًا جيدًا تمامًا للبرامج النصية البسيطة والفريدة حيث لا تكون التكلفة الإضافية لإعداد حلقة حدث غير متزامنة ضرورية وتكون قابلية القراءة هي الأهم.
استخدم `urllib3` إذا...
كنت مؤلف مكتبة وتحتاج إلى إجراء طلبات HTTP بأقل قدر من التبعيات وأقصى قدر من التحكم. بالاعتماد على `urllib3`، تتجنب فرض `requests` أو `httpx` على مستخدميك. يجب عليك أيضًا اللجوء إليها إذا كانت لديك متطلبات محددة جدًا ومنخفضة المستوى لإدارة الاتصال أو TLS لا تكشفها المكتبات عالية المستوى.
الخاتمة
يقدم مشهد عملاء HTTP في بايثون مسارًا تطوريًا واضحًا. توفر `urllib3` المحرك القوي والمتين الذي يدعم النظام البيئي. بنت `requests` على هذا المحرك لإنشاء واجهة برمجة تطبيقات بديهية ومحبوبة لدرجة أنها أصبحت معيارًا عالميًا، مما أتاح الوصول إلى الويب لجيل من مبرمجي بايثون. الآن، تقف `httpx` كوريث حديث، محتفظة بسهولة الاستخدام الرائعة لـ `requests` بينما تدمج الميزات الحاسمة اللازمة للجيل القادم من البرمجيات: العمليات غير المتزامنة وبروتوكولات الشبكة الحديثة.
بالنسبة للمطورين اليوم، أصبح الخيار أوضح من أي وقت مضى. بينما تظل `requests` أداة موثوقة للمهام المتزامنة، فإن `httpx` هو الخيار المستقبلي لجميع التطورات الجديدة تقريبًا. من خلال فهم نقاط قوة كل مكتبة، يمكنك بثقة اختيار الأداة المناسبة للمهمة، مما يضمن أن تطبيقاتك قوية، عالية الأداء، وجاهزة للمستقبل.